home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / dbms_mag / 9104 / cblocks.txt next >
Text File  |  1991-03-01  |  40KB  |  971 lines

  1.                 5.0 FOCUS: Code Blocks for Blockheads, Part 1
  2.                                  By Greg Lief
  3.  
  4. Introduction
  5.  
  6. Now that we have all had more time to play with Clipper 5.0, this
  7. is an appropriate time to talk more about code blocks, along with some
  8. 5.0 functions that use them.
  9.  
  10. Last month we talked about code block basics.  We also looked at some
  11. of the basic code block functions (EVAL(), AEVAL(), ASORT(), ASCAN(),
  12. and DBEVAL()).  There are several other new functions that use these
  13. creatures, and we will look at them now in excruciating detail. We
  14. will also discuss an intriguing method of passing local variables to
  15. another function with code blocks.
  16.  
  17.  
  18. Code Blocks Laid Bare
  19.  
  20. Code blocks are a new datatype that contains compiled Clipper code.
  21. They can be compiled either at compile-time with the rest of your
  22. Clipper code, or at run-time with the use of the & operator.
  23. (Yes, Virginia, I know this does not make a lot of sense, but there
  24. are plenty of examples to follow.)
  25.  
  26. This is a code block in its rawest form:
  27.  
  28.             { | [<argument list>] | <expression list> }
  29.  
  30. Code blocks look quite similar to Clipper 5.0 arrays.  Both code
  31. blocks and arrays begin with an open curly brace ("{") and end with a
  32. closed curly brace ("}").  But code blocks differentiate themselves by
  33. including two "pipe" characters ("|") directly after the opening
  34. brace.  You may optionally include an <argument list> between these
  35. pipe characters, which would then be passed to the code block upon
  36. evaluation.  The <argument list> should be comma delimited (e.g.,
  37. "a, b, c...").
  38.  
  39. Although white space between the pipe characters and braces is purely
  40. optional, I highly recommend you use it for the sake of readability.
  41.  
  42. The <expression list> is, obviously enough, a comma-delimited list of
  43. any valid Clipper expressions.  These can run the gamut, as you
  44. will quickly discover.
  45.  
  46.  
  47. How to Write a Code Block
  48.  
  49. There are three methods in which to write a code block:
  50.  
  51.    a) To be compiled into a code block at compile-time, for example:
  52.  
  53.       local myblock := { | | fname }
  54.  
  55.    b) As a character string, which can be compiled to a code block at
  56.       run time.  For such compilation you can use the & operator
  57.       (yes, the same one that is used for macros).  But remember that
  58.       this is not the same thing as macro substitution!
  59.  
  60.       Suppose that we wanted to set up a TBrowse() object to browse a
  61.       database.  We would need to establish a column for each field in
  62.       the database.  When setting up TBrowse() columns, we must
  63.       specify a code block, which when evaluated, contains the
  64.       contents of that column.  If we knew in advance that our
  65.       database contained the fields FNAME, LNAME, and SSN, it would be
  66.       a simple matter to write the code blocks so that they could be
  67.       compiled at compile-time:
  68.  
  69.       local x, browse := TBrowseNew(3, 19, 15, 60), column
  70.       use test
  71.       column := TBColumnNew("FNAME", { | | fname } )
  72.       browse:AddColumn( column )
  73.       column := TBColumnNew("LNAME", { | | lname } )
  74.       browse:AddColumn( column )
  75.       column := TBColumnNew("SSN", { | | ssn } )
  76.       browse:AddColumn( column )
  77.  
  78.       However, let us further suppose that we wish this routine to be
  79.       generic.  We therefore cannot hard-code field names, because the
  80.       structure will be unknown until run-time.  Here's how we would
  81.       approach it:
  82.  
  83.       local x, browse := TBrowseNew(3, 19, 15, 60), column
  84.       use test
  85.       for x := 1 to fcount()
  86.          column := TBColumnNew(field(x), &("{ | | " + field(x) + "}"))
  87.          browse:AddColumn( column )
  88.       next
  89.  
  90.       The Clipper FIELD() function returns the name of the field based
  91.       at the ordinal position in the database structure.  For example,
  92.       FIELD(2) will return the name of the second field in the
  93.       database ("LNAME" in our little example).
  94.  
  95.    c) Cower in fear at the mention of the words "code block", and let
  96.       the preprocessor write them all for you.  For example, if you
  97.       write the following code:
  98.  
  99.       index on fname to customer
  100.  
  101.       lo and behold!  The preprocessor will dedicate a code block in
  102.       your honor:
  103.  
  104.       __dbCreatIndex( "temp", "fname", {|| fname}, if(.F., .T., NIL))
  105.  
  106.       Code blocks have much in common with inner city cockroaches: you
  107.       cannot neither run nor hide from them.  Thankfully, code blocks
  108.       are a lot more fun and a million times more useful than
  109.       cockroaches, which is why if you have read this far, you should
  110.       keep reading and stop playing with your pet cockroach.
  111.  
  112.  
  113. Evaluating Code Blocks
  114.  
  115. The only operation that you can perform on a code block is evaluation.
  116. You can think of evaluation as being analagous to calling a function
  117. and returning a value from it.  Code blocks are evaluated by the
  118. EVAL(), AEVAL(), or DBEVAL() functions.  They are also evaluated
  119. internally when you pass them as parameters to functions that can use
  120. them.  When evaluated, code blocks return the value of the rightmost
  121. expression within them.  For example, if you create the following code
  122. block:
  123.  
  124.    local myblock := { | | mvar }
  125.  
  126. when you EVALuate this code block, it will return the value of MVAR.
  127.  
  128.    local myblock := { | | mvar }, mvar := 500, x
  129.    x := eval(myblock)
  130.    ? x                        // output: 500
  131.  
  132. Remember that code blocks can contain any valid Clipper expressions.
  133. This means that you can get considerably fancier with them.  For
  134. example:
  135.  
  136.    local myblock := { | | qout(var1), qqout(var2), 500 }
  137.    local var1 := "Mister ", var2 := "Grump"
  138.    x := eval(myblock)              // output: "Mister Grump"
  139.    ? x                             // output: 500
  140.  
  141. Look again at that last statement.  How does X get the value of 500?
  142. When you evaluate a code block, it returns the value of the last (or
  143. rightmost) expression within it.  Because the last expression in
  144. MYBLOCK was 500, the variable X assumed that value.
  145.  
  146.  
  147. Using Code Blocks Without Parameters
  148.  
  149. These are examples of simple code blocks that do not use parameters:
  150.  
  151.    local myblock := { | | qout(mvar) }, mvar := "testing"
  152.    eval(myblock)    // output: "testing"
  153.  
  154.    local myblock := { | | 5000 }
  155.    x := eval(myblock)
  156.    ? x              // output: 5000
  157.  
  158.    local myblock := { | | x++ }
  159.    for y := 1 to 100
  160.       eval(myblock) // crashes because X has not been defined
  161.    next
  162.    ? x
  163.  
  164.    local myblock := { | | x++ }, x := 1  // much nicer thanks
  165.    for y := 1 to 100
  166.       eval(myblock)
  167.    next
  168.    ? x              // output: 101
  169.  
  170.    local myblock := { | | BlueFunc() }
  171.    eval(myblock)   // calls BlueFunc() which displays a message
  172.    return nil
  173.  
  174.    static function bluefunc
  175.    ? "here we are in a BlueFunc() - will we ever escape?"
  176.    inkey(5)
  177.    return nil
  178.  
  179.  
  180. Using Code Blocks with Parameters
  181.  
  182. Just as with functions, there is far greater power to harness with
  183. code blocks when you begin passing parameters.  Writing a parameter
  184. list for a code block is nearly identical to writing one for a
  185. function.  However, because it is harder to conceptualize in the
  186. linear world of a code block, let's write a simple code block and then
  187. rewrite it as a function:
  188.  
  189.    local myblock := { | a, b, c | max(a, max(b, c)) }
  190.  
  191.    function mmax(a, b, c)
  192.    return max(a, max(b, c))
  193.  
  194. As you can readily see, the function MMax() returns the highest of the
  195. three parameters passed to it.  Evaluating the code block MyBlock will
  196. return exactly the same thing.  However, we must first slip past
  197. another stumbling block: namely, how to pass parameters to a code
  198. block.  It is actually quite simple; the EVAL() function accepts
  199. optional parameters after the name of the code block.  Each such
  200. optional parameter represents a parameter to be passed to the code
  201. block.  For example, if you write:
  202.  
  203.    eval(myblock, 20)
  204.  
  205. you are passing the numeric parameter 20 to the code block defined as
  206. MyBlock.  Let's have another look at our MMAX() function and code
  207. block so that you can get a feel for passing parameters with EVAL():
  208.  
  209.    local myblock := { | a, b, c | max(a, max(b, c)) }
  210.    ? mmax(20, 100, 30)             // output: 100
  211.    ? eval(myblock, 20, 100, 30)    // output: 100
  212.  
  213. Do you remember the BlueFunc() that we were just in?  (I'm feeling
  214. much better now, thank you.)  Whaddaya say we modify the function and
  215. the code block to accept a parameter which will dictate how long to
  216. wait for a keypress?
  217.  
  218.    local myblock := { | x | BlueFunc(x) }
  219.    eval(myblock, 20)   // calls BlueFunc() and will wait 20 seconds
  220.    return nil
  221.  
  222.    static function bluefunc(delay)
  223.    ? "we're in a BlueFunc() for " + ltrim(str(delay)) + " seconds"
  224.    inkey(delay)
  225.    return nil
  226.  
  227. Here is a code block that accepts up to three parameters and displays
  228. them on the screen.
  229.  
  230.    local myblock := { | a, b, c | qout(a, b, c) }
  231.    eval(myblock, 1, 2, 3)       // output: 1 2 3
  232.    x := eval(myblock, 1, 2)     // output: 1 2 NIL
  233.    ? x                          // output: NIL
  234.  
  235. You already know why the second EVAL() statement outputs 1, 2, and
  236. NIL, right?  It is because any declared parameters that are not
  237. received are initialized to NIL (see my article in the December 1990
  238. Aquarium on the subject of NIL).  Because MyBlock expects three
  239. parameters (A, B, C), and we only pass two, C gets initialized to NIL.
  240. Trick question: do you know why X takes the value of NIL?  No, it has
  241. nothing to do with the fact that we passed too few parameters. Rather,
  242. it is because the code block returns the value of the expression
  243. QOut(a, b, c).  The QOut() function always returns NIL.  (If you
  244. already knew this, give yourself a pat on the back but do not break
  245. your arm in the process!)
  246.  
  247. Important Note: any arguments that you specify in a code block
  248. are automatically given LOCAL scope.  Such arguments will not be
  249. visible to any nested code blocks!  This merits another example:
  250.  
  251.    local firstblock := { | | qout(x) }
  252.    local myblock := { | x | x++, eval(firstblock) }
  253.    eval(myblock, 3)
  254.  
  255. This program will crash when you attempt to EVALuate FirstBlock().
  256. It does seem that the argument X in MyBlock() should be visible within
  257. FirstBlock().  But X is LOCAL to MyBlock() and is therefore NOT
  258. visible to FirstBlock().
  259.  
  260.  
  261. Functions That Crave Code Blocks
  262.  
  263. EVAL(<block>, [<arg list>])
  264.  
  265.    You should have already surmised that EVAL() evaluates a code
  266.    block, which you pass to it as the <block> parameter.  The optional
  267.    parameter <arg list> is a comma-delimited list of parameters to be
  268.    passed to the code block when you evaluate it.
  269.  
  270.    Return value: EVAL() returns the value of the last (rightmost)
  271.    expression within the block.
  272.  
  273. AEVAL(<array>, <block>, [<start>], [<count>])
  274.  
  275.    AEVAL() is similar to EVAL() but is specially designed to work with
  276.    arrays.  It evaluates a code block (specified by the <block>
  277.    parameter) for each element in the array (specified by the <array>
  278.    parameter).  You may optionally specify a <start> element, and a
  279.    number of elements (<count>) to process.  If you do not use these
  280.    optional parameters, AEVAL() will begin with the first element in
  281.    the array and process all of them.
  282.  
  283.    The following AEVAL() is a real workhorse; it determines the
  284.    maximum, minimum, and sum of all elements in the array MyArray:
  285.  
  286.    local myarray := { 75, 100, 2, 200, .25, -25, 40, 52 }, ;
  287.          nmax, nmin, nsum := 0
  288.    nmax := nmin := myarray[1]
  289.    aeval(myarray, { | a | nmax := max(nmax, a), nmin := min(nmin, a),;
  290.                           nsum += a } )
  291.    ? "Maximum value:", nmax         // 200
  292.    ? "Minimum value:", nmin         // -25
  293.    ? "Total amount: ", nsum         // 444.25
  294.  
  295.    AEVAL() automatically passes two parameters to the code block:
  296.    <value> and <number>.  <value> is the value of the array element
  297.    being processed.  <number> is the number of the array element being
  298.    processed.  You have already seen how <value> is used, but why
  299.    should we bother with <number>?  Suppose that you want to increment
  300.    each element in MyArray.  You would probably write your code block
  301.    like this:
  302.  
  303.    aeval(myarray, { | a | a++ } )
  304.    aeval(myarray, { | a | qout(a) } )
  305.  
  306.    Surprise, surprise!  This will not do a single thing to the
  307.    elements of the array, because they are passed by value (not
  308.    reference) to the code block.  Passing by value means that the
  309.    code block makes a copy of the array element, and any manipulation
  310.    done within the code block is performed on the copy rather than the
  311.    genuine article.  Let's try it again with the <number> parameter:
  312.  
  313.    aeval(myarray, { | a, b | myarray[b]++ } )
  314.    aeval(myarray, { | a | qout(a) } )
  315.  
  316.    Return value: AEVAL() returns a reference to the array you ask it
  317.    to process.
  318.  
  319. DBEVAL(<block>, [<for>], [<while>], [<next>], [<record>], [<rest>])
  320.  
  321.    DBEVAL() is similar to AEVAL(), except that it deals with databases
  322.    rather than arrays.  It also provides far greater control,
  323.    including FOR, WHILE, NEXT, RECORD, and REST clauses.  If you look
  324.    at the STD.CH header file, you will see that the COUNT, SUM, and
  325.    AVERAGE commands, as well as the iterator versions of DELETE,
  326.    RECALL, and REPLACE, are all preprocessed into calls to DBEVAL().
  327.    For example, if you want to sum the field BALANCE for all records in
  328.    your database, the following DBEVAL() would do the trick:
  329.  
  330.    ntotal := 0
  331.    DBEval( { | | ntotal += balance} )
  332.  
  333.    You could easily modify this to keep track of the highest balance:
  334.  
  335.    ntotal := nmax := 0
  336.    DBEval( { | | ntotal += balance, nmax := max(nmax, balance) } )
  337.    ? "Total:  ", ntotal
  338.    ? "Maximum:", nmax
  339.  
  340.    <block> is the code block to evaluate for each database record.
  341.    There are a plethora of optional parameters.
  342.  
  343.    <for> and <while> are code blocks that correspond directly to the
  344.    FOR and WHILE clauses. Basically, if you use either or both of
  345.    these clauses, DBEVAL() will process records until the code blocks
  346.    return False (.F.).
  347.  
  348.    <next> and <record> are both numerics; <next> specifies how many
  349.    records to process from the current record, and <record> specifies
  350.    which record number to process.
  351.  
  352.    <rest> is a logical that determines whether the DBEVAL() scope will
  353.    be from the current record to the end-of-file, or all records.  If
  354.    you pass True (.T.), DBEVAL() will assume that you prefer the
  355.    former (i.e., start from current record).  If you pass False or
  356.    ignore this parameter, DBEVAL() will process all records.
  357.  
  358.    Return Value:  DBEVAL() always returns NIL.
  359.  
  360. ASCAN(<array>, <value>, [<start>], [<count>])
  361.  
  362.    As in Summer '87, ASCAN() scans an array for a given <value>.
  363.    However, the big difference is that you can now pass a code block
  364.    as the <value>!  "Why would I want to do that?" you moan.  Off the
  365.    top of my head comes one example: the case-insensitive ASCAN().
  366.    Try it in Summer '87.  (I don't know how to simulate the passage of
  367.    time in an article like this, but I'll give it my best shot!)
  368.  
  369.    (Three Hours Later)
  370.  
  371.    What?  You mean to tell me that you cannot do a case-insensitive
  372.    ASCAN() in Summer '87?  Gee whiz, no wonder my users were having
  373.    problems!  Thank goodness it takes nothing more than a well-placed
  374.    code block in Clipper 5.0:
  375.  
  376.    ascan(myarray, { | a | upper(a) = upper("search value")} )
  377.  
  378.    This will scan MyArray and test the upper-case equivalent of each
  379.    array element against the upper-case search value.  But before we
  380.    move on, let's bullet-proof this code block.  Do you know what
  381.    happens if you try to convert a non-character value with UPPER()?
  382.    (The answer is... an unexpected DOS holiday.)  So let us ensure
  383.    that each element thus tested is indeed a character string:
  384.  
  385.    ascan(array, { | a | if(valtype(a) == "C", ;
  386.                         upper(a) = upper(value), .F.) } )
  387.  
  388.    An ounce of prevention is worth a day of debugging!
  389.  
  390. ASORT(<array>, [<start>], [<count>], [<block>])
  391.  
  392.    As in Summer '87, ASORT() sorts an array.  The optional parameters
  393.    <start> and <count> are the same here as in AEVAL().  However, as
  394.    with ASORT(), code blocks let you dramatically change the shape of
  395.    things.  You could come up with any manner of arcane sorts: put
  396.    all elements containing the word "Grump" at the top of the array
  397.    (where they should rightfully be); descending order; alphabetical
  398.    order based on the last letter in the word (!).
  399.  
  400.    Each time your code block is evaluated by ASORT(), the function
  401.    passes two array elements to the block.  The block is then expected
  402.    to compare them in some fashion that you specify, and return either
  403.    True (.T.) if the elements are in proper order or False (.F.) if
  404.    they are not.
  405.  
  406.    Here's a descending sort:
  407.  
  408.    local myarray := { "GREG", "JUSTIN", "JENNIFER", "TRACI", "DON" }
  409.    asort(myarray,,, { | x, y | x > y } )
  410.    aeval(myarray, { | a | qout(a) } )  // so you can see it worked!
  411.  
  412.    One situation where a code block sort would save the day is when
  413.    you must sort a multi-dimensional array.  Let's fill an array with
  414.    DIRECTORY() information, and then sort it by filename.  Bear in
  415.    mind that DIRECTORY() returns an array containing one array for
  416.    each file:
  417.  
  418.        Array Element    Information      Manifest Constant
  419.                                          (in DIRECTRY.CH)
  420.              1           file name            F_NAME
  421.              2           file size            F_SIZE
  422.              3           file date            F_DATE
  423.              4           file time            F_TIME
  424.              5           attribute            F_ATTR
  425.  
  426.    In Summer '87, we must rely upon the soon-to-be-put-out-to-pasture
  427.    ADIR() function, which requires that we establish an array for each
  428.    piece of information that we want to capture.
  429.  
  430.    * sort a directory listing by filename
  431.    * first in Summer '87
  432.    private files_[adir("*.*")]
  433.    adir("*.*", files_)
  434.    asort(files_)
  435.  
  436.    * then in 5.0
  437.    local files_ := directory("*.*")
  438.    asort(files_,,, { | x, y | x[1] < y[1] } )
  439.  
  440.    Now let's sort the directory by date:
  441.  
  442.    * Summer '87
  443.    private files_[adir("*.*")], dates_[adir("*.*")]
  444.    adir("*.*", files_, "", dates_)
  445.    asort(dates_)
  446.  
  447.    * 5.0
  448.    local files_ := directory("*.*")
  449.    asort(files_,,, { | x, y | x[3] < y[3] } )
  450.  
  451.    You can see that the Summer '87 code has become increasingly
  452.    convoluted as we add arrays to capture the other information.
  453.    Not only that, but when we sort the DATES_ array, the FILES_ array
  454.    (which contains the filenames) is left unchanged, thus undercutting
  455.    our best efforts.  By stark contrast, we only needed to change two
  456.    digits in the 5.0 code, and did not have to worry about sorting one
  457.    array while leaving another untouched.
  458.  
  459.    For the grand finale, let's sort them again by date and name.
  460.  
  461.    * Summer '87
  462.    * I give up!
  463.  
  464.    * 5.0
  465.    local files_ := directory("*.*")
  466.    asort(files_,,, { | x, y | if( x[3] = y[3], x[1] < y[1], ;
  467.                                                x[3] < y[3] ) } )
  468.    aeval(files_, { | a | qout(padr(a[1], 14), a[3]) } )
  469.  
  470.    (Note the use of PADR() to ensure that all the filenames line up!)
  471.  
  472.    Because of the wonderful DIRECTORY() function, we can easily
  473.    determine if the dates are the same (x[3] = y[3]).  If they
  474.    are, then we will compare the file names (x[1] < y[1]).
  475.    Otherwise, we compare the file dates (x[3] < y[3]).  Yes, it
  476.    can be done in Summer '87, but it would be such a mess that I would
  477.    be afraid to!
  478.  
  479. FIELDBLOCK(<field>)
  480.  
  481.    FIELDBLOCK() is the first of three new functions that return
  482.    "set-get" code blocks. One of the biggest reasons to use this trio
  483.    of functions is to preclude the use of the macro operator. (As you
  484.    might already know, swearing off macros will make your programs run
  485.    faster and look more svelte.)
  486.  
  487.    FIELDBLOCK() returns a code block for a specified field.  The
  488.    parameter <field> is a character string representing the field name
  489.    to refer to.  You can then either retrieve (get) or assign (set)
  490.    the value of <field> by evaluating the code block returned by
  491.    FIELDBLOCK().  If <field> does not exist in the currently active
  492.    work area, FIELDBLOCK() will return NIL.
  493.  
  494.    Note: if the <field> that you pass to FIELDBLOCK() exists in more
  495.    than one work area, FIELDBLOCK()'s return value will correspond
  496.    only to the <field> in the current area.
  497.  
  498.    Here's an example of retrieving the value:
  499.  
  500.    local bblock, mfield := "FNAME"
  501.    dbcreate("customer", { { "FNAME", "C", 10, 0 } })
  502.    use customer
  503.    append blank
  504.    customer->fname := "JOE"
  505.    bblock := fieldblock(mfield)
  506.    ? eval(bblock)     // displays "JOE"
  507.    /* note the dreaded macro alternative */
  508.    ? &mfield          // slow, and simply no longer chic
  509.  
  510.    To assign a value to a field, you merely evaluate the code block
  511.    and pass the desired value as a parameter.  For example:
  512.  
  513.    local bblock, mfield := "FNAME"
  514.    use customer
  515.    bblock := fieldblock(mfield)
  516.    eval(fieldblock(mfield), "Jennifer")
  517.    ? customer->fname     // output: "Jennifer"
  518.    /* note the dreaded macro alternative */
  519.    replace &mfield with "Jennifer"  // ugh!
  520.  
  521.    The function STRUCT() loops through the structure array created by
  522.    DBSTRUCT() and uses FIELDBLOCK() to retrieve the value for each
  523.    field in your database.
  524.  
  525.    function struct(dbf_file)
  526.    local struct, x
  527.    if dbf_file == NIL
  528.       qout("Syntax: struct <dbf_name>")
  529.    elseif ! file(dbf_file) .and. ! file(dbf_file + ".dbf")
  530.       qout("Could not open " + dbf_file)
  531.    else
  532.       use (dbf_file)
  533.       struct := dbstruct()
  534.       qout("Field Name Type Len Dec Contents of First Record")
  535.       for x := 1 to len(struct)
  536.          qout(padr(struct[x, 1], 10), padr(struct[x, 2], 4), ;
  537.               str(struct[x, 3], 3), str(struct[x, 4], 3),    ;
  538.               eval(fieldblock(struct[x, 1])) )
  539.       next
  540.       use
  541.    endif
  542.    return nil
  543.  
  544.    FLYPAPER: Version 1.03 (currently available at press time) requires
  545.    the <field> to be defined at the time that you call FIELDBLOCK().
  546.    This means that if you changed the code to create the block before
  547.    opening the database that contained the field:
  548.  
  549.    * assume no databases are open
  550.    bblock := fieldblock("FNAME")
  551.    use customer
  552.  
  553.    your program will crash with an EVAL() error.  However, you should
  554.    expect this situation to be corrected with the next release.
  555.  
  556. FIELDWBLOCK(<field>, <work area>)
  557.  
  558.    FIELDWBLOCK() is quite similar to FIELDBLOCK().  However, as you
  559.    may have already surmised from the "W" in its name, it allows you
  560.    to refer to a different work area to retrieve or assign the <field>
  561.    value. As with FIELDBLOCK(), the <field> parameter is a character
  562.    string representing the field name to refer to.
  563.  
  564.    The new parameter <work area> is a numeric indicating which work
  565.    area to look for the <field>.
  566.  
  567.    Once again, you can then either retrieve or assign the value of
  568.    <field> by evaluating the code block returned by FIELDWBLOCK().  If
  569.    <field> does not exist in the specified <work area>, FIELDWBLOCK()
  570.    will return NIL.  (Note: FIELDWBLOCK() does not change the active
  571.    work area.)
  572.  
  573.    Here's FIELDWBLOCK() in action.  Note the use of the SELECT()
  574.    function to determine the work areas; this is infinitely preferable
  575.    to hard-coding (and then having to remember) work area numbers.
  576.  
  577.    dbcreate("customer", { { "LNAME", "C", 10, 0 } })
  578.    dbcreate("vendor", { { "LNAME", "C", 10, 0 } })
  579.    use customer new
  580.    append blank
  581.    customer->lname := "CUSTOMER1"
  582.    use vendor new
  583.    append blank
  584.    vendor->lname := "VENDOR1"
  585.    ? eval(fieldwblock("LNAME", select("customer"))) // CUSTOMER1
  586.    ? eval(fieldwblock("LNAME", select("vendor")))   // VENDOR1
  587.    ? eval(fieldwblock("LNAME", select("vendor")), "Grumpfish")
  588.    ? vendor->lname                                  // Grumpfish
  589.  
  590.    As with FIELDBLOCK(), it is quite easy to assign a value to a
  591.    field.  Simply evaluate the code block returned by FIELDWBLOCK()
  592.    and pass the desired value as a parameter.  This is how I changed
  593.    the field LNAME in VENDOR.DBF in the next-to-last line above.
  594.  
  595.    Last month we showed an example of creating a generic TBrowse object
  596.    to browse a database. FIELDWBLOCK() offers a different solution:
  597.  
  598.    <M>local x, browse := TBrowseDB(3, 19, 15, 60), column
  599.    use test
  600.    for x := 1 to fcount()
  601.       column := TBColumnNew(field(x), fieldwblock(field(x), select()))
  602.       browse:AddColumn( column )
  603.    next<M>
  604.  
  605.    FLYPAPER: In exactly the same fashion as for FIELDBLOCK(),
  606.    version 1.03 (currently available at press time) requires the
  607.    <field> to be defined at the time that you call FIELDWBLOCK().
  608.    If not, your program will crash with an EVAL() error.  However, you
  609.    should expect this situation to be corrected with the next release.
  610.  
  611.    In addition to this idiosyncrasy, FIELDWBLOCK() has problems when
  612.    the <field> is not available in the current work area (regardless
  613.    of which <work area> you specified).  If you specify a <work area>
  614.    other than the current work area, and the <field> is not defined in
  615.    the current work area but is defined in the specified <work area>,
  616.    FIELDWBLOCK() will mistakenly return NIL.  I admit that this is a
  617.    bit confusing, so let me illustrate it with a code snippet:
  618.  
  619.    dbcreate("customer", { { "LNAME", "C", 10, 0 } })
  620.    dbcreate("vendor", { { "LNAME", "C", 10, 0 } })
  621.    use customer new                 // we are now in area 1
  622.    use vendor new                   // we are now in area 2
  623.    ? eval(fieldwblock("LNAME", 1))  // this works flawlessly
  624.    select 0                         // we are now in area 3
  625.    ? eval(fieldwblock("LNAME", 1))  // ouch!!
  626.  
  627.    When you attempt to EVALuate that last line, your program will
  628.    crash gracelessly with the soon-to-be-infamous EVAL() internal
  629.    error 612.
  630.  
  631.    However, expect both of these problems to be corrected with the
  632.    next release of Clipper 5.0.
  633.  
  634. MEMVARBLOCK(<memvar>)
  635.  
  636.    MEMVARBLOCK() is also quite similar to FIELDBLOCK(), except that it
  637.    operates upon memory variables rather than database fields.
  638.    MEMVARBLOCK() returns a code block for a memory variable as
  639.    specified by the <memvar> parameter.  You can then either retrieve
  640.    the value of <memvar> by evaluating the code block returned by
  641.    MEMVARBLOCK(), or assign <memvar> a value by evaluating the code
  642.    block and passing the value as a parameter.
  643.  
  644.    If the <memvar> does not exist, MEMVARBLOCK() will return NIL.
  645.    Important Note: if the <memvar> is either STATIC or LOCAL,
  646.    MEMVARBLOCK() will also return NIL.  This is because MEMVARBLOCK()
  647.    can only operate on variables whose names are known at run-time
  648.    (namely, PRIVATEs and PUBLICs).
  649.  
  650.    In this example, MEMVARBLOCK() retrieves the value of each of four
  651.    memory variables.
  652.  
  653.    // note PRIVATE declaration -- MEMVARBLOCK() doesn't like LOCALs
  654.    private mtot1 := 75, mtot2 := 400, mtot3 := 30, mtot4 := 205, x
  655.    for x := 1 to 4
  656.       ? eval(memvarblock("mtot" + str(x, 1)))
  657.    next
  658.  
  659. SETKEY(<key>, [<block>])
  660.  
  661.    If you used the Summer '87 SET KEY command to establish "hot-key"
  662.    procedures, you may have been frustrated at the inability to
  663.    elegantly manage your hot keys.  For example, if you wanted to turn
  664.    off all hot keys while the user was in a hot key procedure, it
  665.    required a certain degree of tedious coding.
  666.  
  667.    Hot key procedures are yet another area where Clipper 5.0 gives you
  668.    unprecedented control.  Whenever you establish a "hot-key"
  669.    procedure with the SET KEY command, you are basically attaching a
  670.    code block to that keypress with the new SETKEY() function.
  671.  
  672.    SETKEY() allows you to poll any INKEY() value to determine whether
  673.    a code block is attached to it.  Like the other SET() functions, it
  674.    also permits you to change the current setting, i.e., attach a code
  675.    block to any key.
  676.  
  677.    The <key> parameter is a numeric corresponding to the INKEY() value
  678.    of the keypress.  (Please refer to your Clipper documentation, or
  679.    header file INKEY.CH, for a complete listing of INKEY() values.)
  680.  
  681.    The optional <block> parameter is the code block to be evaluated if
  682.    the <key> is pressed during a wait state.  Wait states include
  683.    ACHOICE(), DBEDIT(), MEMOEDIT(), ACCEPT, INPUT, READ, WAIT, and
  684.    MENU TO.  (See below for discussion on INKEY(), the black sheep of
  685.    the wait state family.)
  686.  
  687.    SETKEY() either returns a code block if one is tied to the <key>,
  688.    or NIL.  If you pass the <block> parameter, it will attach that
  689.    code block to the <key>.
  690.  
  691.    The SET KEY command
  692.  
  693.    Before I show you any SETKEY() examples, let us first look at how
  694.    the SET KEY command is handled in 5.0:
  695.  
  696.    set key 28 to helpdev
  697.  
  698.    gets translated by the preprocessor into the following:
  699.  
  700.    SetKey( 28, {|p, l, v| helpdev(p, l, v)} )
  701.  
  702.    The P, L, and V parameters correspond to PROCNAME() (procedure
  703.    name), PROCLINE() (current source code line number), and READVAR()
  704.    (variable name), which will automatically be passed to the code
  705.    block when it is evaluated. (Yes indeed, these are the same
  706.    parameters passed to hot key procedures in Summer '87.) However,
  707.    you can omit these arguments in your code block declaration if you
  708.    will not be using them therein. By the same token, you are
  709.    completely free to pass entirely different parameters to the
  710.    function. (I'll use this technique to pass local variables via code
  711.    blocks a bit later.)
  712.  
  713.    Whenever you come to a Clipper wait state, your keypress will be
  714.    evaluated in approximately this fashion to determine whether or not
  715.    there is a hot-key procedure tied to it:
  716.  
  717.    keypress := inkey(0)
  718.    if setkey(keypress) != NIL
  719.       eval(setkey(keypress))
  720.    endif
  721.  
  722.    SETKEY() := Better Housekeeping
  723.  
  724.    Here is a good example.  Suppose that within a hot key procedure
  725.    you wish to temporarily attach a hot key definition to the F10
  726.    keypress.  However, you may have F10 activating various different
  727.    procedures throughout the course of your program.  In Summer '87,
  728.    this presented a big problem because you were unable to determine
  729.    what procedure was tied to F10, and you would therefore be unable
  730.    to change it and expect to reset it properly.  This is no longer a
  731.    problem with SETKEY().  In this example, we redefine F10 to call
  732.    BLAHBLAH(), and reset it when we are finished.
  733.  
  734.    #include "inkey.ch"     // for INKEY() constants
  735.    function test(p, l, v)
  736.    local old_f10 := setkey(K_F10, { | p,l,v | blahblah(p, l, v)} )
  737.    * main code goes here
  738.    setkey(K_F10, old_f10)                // restore F10 hot key
  739.    return nil
  740.  
  741.    OLD_F10 is assigned the code block (if any) that is attached to
  742.    F10.  F10 is then reassigned to trigger BLAHBLAH().  When we
  743.    prepare to exit, we re-attach the previous code block (stored in
  744.    OLD_F10) to the F10 keypress.  (Once again, please remember that
  745.    you can omit the P, L, V arguments in your code block declaration
  746.    if you will not be using them in the hot key function.)
  747.  
  748.    Important Note: before you go hog wild with hot keys, you should
  749.    know that there are a limit of 32 SETKEY() (or SET KEY, same
  750.    difference) procedures at any given time.
  751.  
  752.    INKEY() := Wait State?
  753.  
  754.    As with Summer '87, INKEY() is not a bona fide wait state.  But as
  755.    you have just seen, SETKEY() makes it very easy to create your own
  756.    INKEY() wait state.  Here's GINKEY() from the Grumpfish Library:
  757.  
  758.    //   GINKEY(<delay>) - INKEY() wait state
  759.    //   Copyright (c) 1990 Greg Lief
  760.    function ginkey(waittime)
  761.    local key := inkey(waittime), cblock
  762.    cblock := setkey(key)
  763.    if cblock != NIL  // there is a code block for this key
  764.       eval(cblock, procname(1), procline(1), 'ginkey')
  765.    endif
  766.    return key
  767.  
  768.    As mentioned earlier, the third parameter passed to hot key
  769.    procedures is the name of the variable being read. In this
  770.    function, "GINKEY" is serving as a dummy variable name. Please feel
  771.    free to change it to anything you desire. If you really wanted to,
  772.    you could pass a variable name as a second parameter to GINKEY(),
  773.    and in turn pass that to the code block if/when it was evaluated.
  774.  
  775.    Notice that when the code block is evaluated, instead of passing it
  776.    the current procedure name and line number, I pass it the
  777.    information that is one level previous on the activation stack.
  778.    (I'll discuss the activation stack in more detail in the March
  779.    Aquarium.)  Otherwise, the hot key procedure would always think
  780.    that it had just come from GINKEY().  This would in turn louse
  781.    things up by forcing you to have the same help screen for every
  782.    GINKEY() wait state.  In fact, there is a problem in Clipper 5.0
  783.    related to the MENU TO wait state, which brings us to the next
  784.    topic of discussion.
  785.  
  786.    MENU TO Caveat
  787.  
  788.    When you trigger a hot-key procedure from a MENU TO statement, the
  789.    wrong PROCNAME() will be sent to the code block.
  790.  
  791.    function main
  792.    local sel
  793.    set key 28 to test
  794.    cls
  795.    @ 12,0 prompt "option 1"
  796.    @ 13,0 prompt "option 2"
  797.    @ 14,0 prompt "option 3"
  798.    @ 15,0 prompt "option 4"
  799.    menu to sel
  800.    return nil
  801.  
  802.    function test(p,l,v)
  803.    ? p            // __MENUTO (wrong)
  804.    ? procname(1)  // (b)MAIN
  805.    ? procname(2)  // __MODALKEY
  806.    ? procname(3)  // __MENUTO
  807.    ? procname(4)  // MAIN     (right)
  808.    inkey(0)
  809.    return nil
  810.  
  811.    To determine the proper name where the MENU TO statement is
  812.    located, you must jump back by four levels of nesting. You can see
  813.    that the procedure name is off by one level of nesting. Instead of
  814.    PROCNAME(3), the MENU wait state should be sending PROCNAME(4) to
  815.    the code block.
  816.  
  817.    Let's have a closer look at the callstack created by the MENU TO
  818.    command. The first procedure name represents where the code block
  819.    was actually created. The "(b)" prefix denotes that the procedure
  820.    name is part of the callstack only because Clipper had to jump back
  821.    momentarily to review the definition of the code block. (Remember
  822.    this when you see "(b)" in conjunction with a run-time error.)
  823.  
  824.    The second procedure name is __MODALKEY, an internal Clipper
  825.    function that apparently processes keystrokes. The third is
  826.    __MENUTO, which is mistakenly passed to any hot key procedures
  827.    triggered from its wait state.
  828.  
  829.    Here is the official Nantucket workaround.  Place this at the top
  830.    of any procedure that is likely to be executed from setkey():
  831.  
  832.    function whatever(cproc, nline, cvar)
  833.    if procname(3) == "__MENUTO"
  834.      cproc := procname(4)
  835.    else
  836.      cproc := procname(3)
  837.    endif
  838.  
  839.    Here is another suggestion that makes use of the preprocessor.
  840.    Place the following in a header (.CH) file:
  841.  
  842.    #translate FIXMENU(<proc>, <line>) => ;
  843.       <proc> := procname(if(procname(3) = '__MENUTO', 4, 3)) ;;
  844.       <line> := procline(if(procname(3) = '__MENUTO', 4, 3))
  845.  
  846.    Then be sure to include this header file and the following line at
  847.    the top of your setkey() procedures:
  848.  
  849.    #include "whatever.ch"
  850.  
  851.    function help(p, l, v)
  852.    FIXMENU(p, l)
  853.    etcetera
  854.  
  855. Passing LOCAL Variables in a Code Block
  856.  
  857. You and I both know that the scope of a LOCAL variable is the
  858. procedure or function in which it is declared.  But there is actually
  859. a way to pass a LOCAL to a different function.  It requires the use of
  860. a code block.  Watch this!
  861.  
  862.    function main
  863.    local bblock := { | | x }, x := 500
  864.    test1(bblock)
  865.    return nil
  866.  
  867.    function test1(b)
  868.    ? eval(b)   // output: 500
  869.    return nil
  870.  
  871. When BBLOCK is compiled in MAIN(), it will contain a reference to X,
  872. which is a variable local to MAIN().  However, when the block BBLOCK
  873. is passed as a parameter to TEST1(), and subsequently evaluated
  874. therein, X's value will indeed be available.
  875.  
  876. Mind you, I do not advocate the unmitigated use of this technique.
  877. It does not seem to be exactly what the architects had in mind for
  878. LOCAL variables, eh?  But one situation comes to mind where this
  879. method saved the day for me.  I wanted to GET a variable, and allow
  880. the user to press a hot key to pop up a list of valid entries.  This
  881. sounds pretty simple, doesn't it?  It would be, except that the
  882. variable in question was LOCAL and thus restricted in scope to the
  883. function in which I was GETting it.  What to do... what to do?  Here
  884. is how I solved the problem with the clever use of a code block:
  885.  
  886.   #include "inkey.ch"
  887.   #include "box.ch"
  888.  
  889.   function test
  890.   local mvalue := space(7), oldaltv, x
  891.   memvar getlist
  892.   if ! file("lookup.dbf")
  893.      dbcreate("lookup", { { "LNAME", "C", 7, 0 } } )
  894.      use lookup
  895.      for x := 1 to 9
  896.         append blank
  897.         /* note use of unnamed array -- it works just fine this way */
  898.         replace lookup->lname with { "BOOTH", "DONNAY", "FORCIER", ;
  899.              "LIEF", "MAIER", "MEANS", "NEFF", "ROUTH", "YELLICK" }[x]
  900.      next
  901.   else
  902.      use lookup
  903.   endif
  904.   /* note that I pass MVALUE by reference to VIEW_VALS() below */
  905.   oldaltv := setkey( K_ALT_V, {| | View_Vals(@mvalue)} )
  906.   setcolor('+gr/b')
  907.   cls
  908.   @ 4, 28 say "Enter last name:" get mvalue
  909.   setcolor('+w/b')
  910.   @ 5, 23 say '(press Alt-V for available authors)'
  911.   read
  912.   quit
  913.  
  914.   static function view_vals(v)
  915.   local browse, column, key, marker := recno(),                   ;
  916.         oldscrn := savescreen(8, 35, 20, 44, 2),                  ;
  917.         oldcolor := setcolor("+W/RB"), oldcursor := setcursor(0), ;
  918.         oldblock := setkey( K_ALT_V, NIL )  // turn off ALT-V
  919.   @ 8, 35, 20, 44 box B_SINGLE + chr(32)
  920.   browse := TBrowseDB(9, 36, 19, 43)
  921.   browse:headSep := "═"
  922.   browse:colorSpec := '+W/RB, +W/N'
  923.   column := TBColumnNew( "Author", FieldBlock("lname") )
  924.   browse:addColumn(column)
  925.   go top
  926.   do while .t.
  927.      do while ! browse:stabilize() .and. (key := inkey()) = 0
  928.      enddo
  929.      if browse:stable
  930.         key := inkey(0)
  931.      endif
  932.      do case
  933.         case key == K_UP
  934.            browse:up()
  935.         case key == K_DOWN
  936.            browse:down()
  937.         case key == K_ESC .or. key == K_ENTER
  938.            exit
  939.      endcase
  940.   enddo
  941.   if lastkey() != K_ESC
  942.      /*
  943.        because we passed the variable BY REFERENCE in the code block,
  944.        any changes we make here are being made to the actual variable,
  945.        and that is the key to this whole mess working the way it does!
  946.      */
  947.      v := eval(fieldblock('lname'))
  948.   endif
  949.   go marker
  950.   restscreen(8, 35, 20, 44, oldscrn)
  951.   setcolor(oldcolor)
  952.   setcursor(oldcursor)
  953.   setkey(K_ALT_V, oldblock)   // reset Alt-V for next time
  954.   return nil
  955.  
  956. Conclusion
  957.  
  958. I certainly hope that this article has shattered any mental blocks that
  959. you may have had about code blocks. Like it or not, code blocks are an
  960. integral (and inescapable) part of Clipper 5.0.  Even if you never
  961. explicitly write a code block in your code, you can bet that the
  962. preprocessor will be turning your commands into code blocks, so you
  963. might as well grin and bear it, and learn how to use code blocks to your
  964. great advantage. As with most things in Clipper 5.0, your imagination
  965. should be your only limit when dealing with code blocks.
  966.  
  967. About The Author
  968.  
  969. Greg Lief is co-authoring a book on Clipper 5.0 with Craig Yellick and
  970. Joe Booth for Howard Sams.
  971.